Lobster Thermal Preference and GLORYS Bottom Temperature
Author
Adam Kemberling
Published
November 15, 2024
Code
# Librarieslibrary(tidyverse)library(gmRi)library(rnaturalearth)library(scales)library(sf)library(gt)library(patchwork)library(ggdist)library(ggimage)library(tidyterra)library(terra)library(virtualspecies)conflicted::conflict_prefer("select", "dplyr")conflicted::conflict_prefer("filter", "dplyr")# ggplot themetheme_set(theme_gmri(axis.line.y =element_line(),axis.ticks.y =element_line(), rect =element_rect(fill ="transparent", color =NA),panel.grid.major.y =element_blank(),strip.text.y =element_text(angle =0),axis.text.x =element_text(size =8),axis.text.y =element_text(size =8),legend.position ="bottom") +theme(plot.background =element_rect(fill ="transparent", color ="Black"),panel.border =element_rect(fill ="transparent", color ="black", linewidth =2), legend.title.position ="top" ))# Degree symboldeg_c <-"\u00b0C"# Gulf of Mainegom_bounds <-read_sf( gmRi::get_timeseries_paths(region_group ="gmri_sst_focal_areas", box_location ="cloudstorage")$apershing_gulf_of_maine$shape_path)# Make a box to use when cropping based on an xlim and ylim pairmake_cropbox <-function(xlims, ylims){ sfc <-st_sfc(st_polygon(list(rbind(c(xlims[[1]], ylims[[1]]), c(xlims[[1]], ylims[[2]]), c(xlims[[2]], ylims[[2]]), c(xlims[[2]], ylims[[1]]), c(xlims[[1]], ylims[[1]]))))) sfc <-st_as_sf(sfc)return(sfc)}# Temp palette:temp_pal <-rev(RColorBrewer::brewer.pal(n =10, name ="RdBu"))new_england <-ne_states("united states of america", returnclass ="sf")canada <-ne_states("canada", returnclass ="sf")
Contents:
Progression of Temperature Change in NE Atlantic (annual, seasonal)
Representation of Warming Rates
Thermal habitat changes for lobster
Prepare Monthly Temperature Data
From a thermal preference angle, the suitability of certain habitats to lobster will change based on bottom temperature.
Now that we have access to freely available ocean reanalysis data it is possible to explore high resolution differences in bottom habitat suitability throughout the year.
# Load GLORYSgpath =cs_path("res", "GLORYs/NE_Shelf_TempSal_Monthly")# Load the monthly averages and the climatology# Done in: py/Monthly_Surface_and_Bottom.ipynb# Temperature and anomaliestemps_monthly <- terra::rast(str_c(gpath, "Northwest_Atlantic_Surface_Bottom_93to2022_anoms.nc"))# # Layer sources# terra::sources(temps_monthly)# terra::varnames(temps_monthly)# Datesr_dates <- terra::time(temps_monthly)# # Why does it stack them this way?# which(year(r_dates) == "2000" & month(r_dates) == 6)# # climatology# glorys_clim <- terra::rast(# str_c(gpath,"Northwest_Atlantic_Surface_Bottom_93to2022_clim.nc"))
Code
# Define the bounding box as an sf object# Cropping Extent crop_extent <-make_cropbox(xlims =c(-79, -56), ylims =c(32, 51))# Crop ittemps_cropped <- terra::crop(temps_monthly, crop_extent)#varnames(temps_cropped)bt_anom <- temps_cropped["bottom_temp_anom"]sst_anom <- temps_cropped["surface_temp_anom"]sst <- temps_cropped["surface_temp"][1:372]# Split them into separate variables# Find indices of layers that belong to "sst"layer_names <-names(temps_cropped)sst_indices <-which(str_detect(layer_names, "surface_temp"))sst_anom_indices <-which(str_detect(layer_names, "surface_temp_anom"))bt_indices <-which(str_detect(layer_names, "bottom_temp"))bt_anom_indices <-which(str_detect(layer_names, "bottom_temp_anom"))# and depthdepth_indices <-which(str_detect(layer_names, "depth"))# Subset the variables out separately by names`%notin%`<-negate(`%in%`)sst_indices <- sst_indices[which(sst_indices %notin% sst_anom_indices)]bt_indices <- bt_indices[which(bt_indices %notin% bt_anom_indices)]# Indexing is THE worstsst <- temps_cropped[[sst_indices]]bt <- temps_cropped[[bt_indices]]sst_anom <- temps_cropped[[sst_anom_indices]]bt_anom <- temps_cropped[[bt_anom_indices]]depths <- temps_cropped["depth"][[1]]# Assign dates as the namesnames(sst) <-time(sst)names(bt) <-time(bt)names(sst_anom) <-time(sst_anom)names(bt_anom) <-time(bt_anom)# Take one depth layer to make mask for values over limit NAbot_depth <- depths[[1]]over_depths <-which(values(bot_depth) >1450)bot_depth[over_depths] <-NA# # Plot the depth# plot(bot_depth, main = "Max Depths")
Knowing that the temperatures globally are changing, it is logical to anticipate species to adjust their movements and behaviors to follow their thermal preferences.
From historical datasets and experiments we are able to determine the range of temperatures that species need and/or prefer in order to live.
Knowing these limits then lets us project onto a map where those conditions exist and where conditions are less-tolerable.
Lobster are a relatively well studied species, and it is believed that they prefer a range of temperatures between 10 and 20C. The effects of exposure to temperatures on either side of this range has an asymmetry such that lower temperatures are less of an issue than warmer ones. Below 10C it is understood that lobster activity and metabolism is lower, but they are otherwise unharmed. Above 20C temperatures have a stressful effect and can lead to disease and mortality.
Code
# Get temperature range for study area as a vectortemp_range <-seq(min(values(temps_monthly["bottom_temp"]), na.rm = T),max(values(temps_monthly["surface_temp"]), na.rm = T),by = .1)# Use the betaFun(), feed it temperature vector min/max and curve shapelob_beta <-betaFun(x = temp_range,p1 =0, p2 =20, alpha =4, gamma =0.7)# Show the temp preferenceplot( lob_beta ~ temp_range, type ="l", main ="Hypothetical Lobster Temperature Preference Curve", ylab ="Preference", xlab ="Temperature Range")
Decadal Preference Maps
Lobster have a thermal preference (lab venture) of 11-22C.
We can get a little more granular and apply that thermal preference window to monthly data. Below I’ve used 2023 as an example. This could also be done with daily data.
Lobsters are thought to be relatively inactive below 10C. If we use this threshold we can map how activity appears spatially.
Use a logistic function centered on 10C, we can get a sense of how active lobsters are likely to be based on monthly bottom temperatures.
This could be adapted down the line to mark when* certain areas had their growing seasons begin and possibly some insight into their movements inshore/offshore.
For the below maps I am applying this logistic function to monthly bottom temperatures:
Code
# Use the logisticFun(), feed it temperature vector min/maxlob_activity <-logisticFun(x = temp_range, alpha =-2, beta =10)plot(lob_activity ~ temp_range, type ="l", main ="Lobster Minimum-Active Temperature", ylab ="Activity Level", xlab =expression("Temperature"~degree~C))
Code
# Function to make preference rasters:logistic_pref_ras <-function(in_ras, ras_name, alpha =-2, beta =10){# Make a new raster that we can swap values from pref_ras <- in_ras[[ras_name]]# Assign values based on preference curvevalues(pref_ras) <-logisticFun(values(pref_ras),alpha = alpha, beta = beta)return(pref_ras)}# Apply the activity logistic functionlob_bot_activity <-map(.x =setNames(names(bt), names(bt)),function(x){ active_ras <-logistic_pref_ras(in_ras = bt, ras_name = x, alpha =-2, beta =10)# Mask depths below our downloaded depth limit active_ras[over_depths] <-NAreturn(active_ras) })
Lobsters are also thought to experience stress when under temperatures above 20C. At temperatures at or near these levels lobsters are likely to attempt to avoid these unfavorable conditions.
We can flip the logistic function and re-center it at/above 20C to then get a sense of where bottom temperatures may be too high.
For the below maps I am applying this logistic function to monthly bottom temperatures:
Code
# Use the logisticFun(), feed it temperature vector min/maxlob_activity <-logisticFun(x = temp_range, alpha =-2, beta =21)plot(lob_activity ~ temp_range, type ="l", main ="Lobster Thermal Stress Limit", ylab ="Stress Level", xlab =expression("Temperature"~degree~C))